package com.prawn.authority.filter;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.prawn.authority.dao.FunctionDao;
import com.prawn.authority.dao.UserDao;
import com.prawn.authority.pojo.Function;
import com.prawn.authority.pojo.User;
import com.prawn.authority.pojo.UserDetailsImpl;
import com.prawn.authority.service.FunctionService;
import com.prawn.authority.utils.ResourceApplicationContext;
import com.prawn.authority.utils.ResponseUtil;
import com.prawn.authority.utils.SecurityContextUtil;
import entity.Result;
import entity.StatusCode;
import io.jsonwebtoken.Claims;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.StringUtils;
import util.JwtUtil;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;
import java.util.regex.Pattern;

/**
 * JWT接口请求校验拦截器
 * 通过继承BasicAuthenticationFilter来实现过拦截功能
 * 请求接口时会进入这里验证Token是否合法和过期，并解析Token里的角色、权限
 */

public class JWTAuthenticationTokenFilter extends BasicAuthenticationFilter {

	private JwtUtil jwtUtil;
	private FunctionService functionService;
	private FunctionDao functionDao;
	private UserDao userDao;


	public JWTAuthenticationTokenFilter(AuthenticationManager authenticationManager) {
		super(authenticationManager);
		inject();
	}

	// Fiter无法使用@Autowried注入
	// 手动注入Bean
	private void inject() {
			this.jwtUtil = ResourceApplicationContext.getApplicationContext().getBean(JwtUtil.class);
			this.functionService = ResourceApplicationContext.getApplicationContext().getBean(FunctionService.class);
			this.functionDao = ResourceApplicationContext.getApplicationContext().getBean(FunctionDao.class);
			this.userDao = ResourceApplicationContext.getApplicationContext().getBean(UserDao.class);
	}

	@Override
	protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException {
		String requestURI = request.getRequestURI();
		try {
			// 这四个uri不用经过处理
			if (!requestURI.equals("/user/login") &&
					!requestURI.equals("/user/getPhotoByIds") &&
					!requestURI.equals("/admin/login") &&
					!Pattern.matches("/email/.+?\\.com", requestURI) &&
					!Pattern.matches("/captcha/.+?", requestURI) &&
					!Pattern.matches("/user/register/.+", requestURI) &&
					!Pattern.matches("/user/retrievePassword/.+", requestURI)) {
				// 获取请求头中JWT的Token
				String tokenHeader = request.getHeader("Authorization");
				if (null != tokenHeader && tokenHeader.startsWith("Bearer ")) {
					// 截取JWT前缀
					String token = tokenHeader.replace("Bearer ", "");
					// 解析JWT
					Claims claims = jwtUtil.parseJWT(token);

					// 获取过期时间
					Date expiration = claims.getExpiration();

					// 若距离过期时间小于5分钟，刷新token
					if (expiration.getTime() - System.currentTimeMillis() < 300000) {
						token = jwtUtil.updateJWT(token);
					}

					// 每次访问接口总是返回未过期或者刷新过的token
					// 把token放入相应体中
					ResponseUtil.placeToken(response, token);

					// 获取token里面的信息，再进行封装成一个UserDetails实例
					String userId = claims.getId();
					if (!StringUtils.isEmpty(userId)) {
						User user = this.userDao.findByUserId(userId);
						List<Function> functionList;
						if (!StringUtils.isEmpty(user.getBaseId()) && user.getBaseIdentity() > 2) {
							functionList = this.functionDao.findFunctionByBase(user.getBaseId(), user.getBaseIdentity());
						} else {
							functionList = this.functionService.findByUserId(userId);
						}

						List<GrantedAuthority> authorities = new ArrayList();
						if (functionList != null && functionList.size() != 0) {
							functionList.forEach(function -> authorities.add(new SimpleGrantedAuthority(function.getName())));
						}

						// 组装参数
						UserDetailsImpl userDetailsImpl = new UserDetailsImpl();
						userDetailsImpl.setUsername(claims.getSubject());
						userDetailsImpl.setUserId(claims.getId());
						userDetailsImpl.setAuthorities(authorities);
						// 对用户进行认证，直接把用户的信息和用户的权限直接交给Authentication的实例
						UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetailsImpl, userId, authorities);
						// Authentication放入安全上下文中
						SecurityContextUtil.setAuthentication(authentication);
					}
				} else {
					// 抛出后被下面的catch捕获
					throw new Exception();
				}
			}
			// 放行
			filterChain.doFilter(request, response);
		} catch (Exception e) {
			// filter抛出的异常不会被全局异常处理类捕获
			// 直接在这里把返回的json写到response里
			ResponseUtil.write(response, JSON.toJSONString(new Result(false, StatusCode.ERROR, "未登录")));
		}
	}
}